Technology#
gdsfactory includes a default Technology in gdsfactory.tech that you can use as an inspiration to create your own.
Layers#
A GDS has different layers to describe the different fabrication process steps.
GDS layers have 2 integer numbers: GDSlayer, GDSpurpose
Klayout shows Layers with a color, style and transparency when showing GDS layouts.
[1]:
import gdsfactory as gf
gf.CONF.plotter = 'holoviews'
2022-03-01 18:39:14.820 | INFO | gdsfactory.config:<module>:52 - Load '/home/runner/work/gdsfactory/gdsfactory/gdsfactory' 4.2.16
[2]:
c = gf.layers.LAYER_SET.preview()
c.plot()
[2]:
[3]:
layer_wg = gf.LAYER.WG
print(layer_wg)
(1, 0)
Remove layers#
You can remove layers using the remove_layers() function.
[4]:
removed = c.remove_layers(layers=(gf.LAYER.WG, gf.LAYER.WGN))
removed.plot()
[4]:
Remap layers#
You can remap (change the polygons from one layer to another layer) using the remap_layer, which will return a new Component
[5]:
c = gf.components.straight(layer=(2,0))
c.plot()
[5]:
[6]:
remap = c.remap_layers(layermap={(2, 0): gf.LAYER.WGN})
remap.plot()
[6]:
Extract layers#
You can also extract layers using the extract function. This function returns a new flattened Component that contains the extracted layers. A flat Component does not have references, and all the polygons are absorbed by the top cell.
[7]:
extract = c.extract(layers=(gf.LAYER.M1,))
extract.plot()
bokeh backend could not plot any Elements in the Overlay.
[7]:
:Overlay
Custom cross_section#
You can create a CrossSection from scratch or you can customize the cross_section functions in gf.cross_section
[8]:
import gdsfactory as gf
strip2 = gf.partial(gf.cross_section.strip, layer=(2, 0))
[9]:
c = gf.components.straight(cross_section=strip2)
c.plot()
[9]:
[10]:
import gdsfactory as gf
pin = gf.partial(
gf.cross_section.strip,
sections=(
gf.tech.Section(width=2, layer=gf.LAYER.N, offset=+1),
gf.tech.Section(width=2, layer=gf.LAYER.P, offset=-1),
),
)
c = gf.components.straight(cross_section=pin)
c.plot()
[10]:
How can you group all components to test them?
You can create a dict of functions and group them
factory = dict(
straight_heater_metal = gf.components.straight_heater_metal,
bend = gf.components.bend_euler
)
[11]:
import gdsfactory as gf
c = gf.components.straight(layer=(41, 0))
c.plot()
[11]:
[12]:
cross_section = gf.cross_section.nitride
wg = gf.components.straight(cross_section=cross_section)
gc = gf.components.grating_coupler_elliptical_te(layer=(34, 0), wg_width=1.0)
wg_gc = gf.routing.add_fiber_single(
component=wg, grating_coupler=gc, cross_section=gf.cross_section.nitride
)
wg_gc.plot()
[12]:
FabA#
Lets create some component factories for FabA
FabA only has one Metal layer available that is defined in GDS layer (30, 0)
The metal layer traces are 2um wide
[13]:
import gdsfactory as gf
from gdsfactory.cross_section import strip
from gdsfactory.tech import Layer
WIDTH = 2
LAYER = (34, 0)
fab_a_metal = gf.partial(strip, width=WIDTH, layer=LAYER)
fab_a_metal.__name__ = "fab_a_metal"
straight = gf.partial(gf.components.straight, cross_section=fab_a_metal)
bend_euler = gf.partial(gf.components.bend_euler, cross_section=fab_a_metal)
mmi1x2 = gf.partial(
gf.components.mmi1x2,
cross_section=fab_a_metal,
width=WIDTH,
width_taper=WIDTH,
width_mmi=3 * WIDTH,
)
mzi = gf.partial(gf.components.mzi, cross_section=fab_a_metal, splitter=mmi1x2)
gc = gf.partial(
gf.components.grating_coupler_elliptical_te, layer=LAYER, wg_width=WIDTH
)
def test_waveguide():
c = gf.components.straight(cross_section=fab_a_metal)
difftest(c)
c = mzi()
c_gc = gf.routing.add_fiber_array(
component=c, grating_coupler=gc, cross_section=fab_a_metal
)
c_gc.plot()
[13]:
you can also add a cell decorator in case you want to add pins or device recognition layers
[14]:
import gdsfactory as gf
from gdsfactory.add_pins import add_outline, add_pins
from gdsfactory.cross_section import strip
from gdsfactory.difftest import difftest
WIDTH = 2
LAYER = (34, 0)
fab_a_metal = gf.partial(strip, width=WIDTH, layer=LAYER)
def test_waveguide():
c = gf.components.straight(cross_section=fab_a_metal)
difftest(c)
def decorator(component) -> None:
"""Fab specific functions over a component."""
add_pins(component)
add_outline(component)
mmi2x2 = gf.partial(gf.components.mmi2x2, decorator=decorator)
mmi1x2 = gf.partial(gf.components.mmi1x2, decorator=decorator)
bend_euler = gf.partial(gf.components.bend_euler, decorator=decorator)
straight = gf.partial(gf.components.straight, decorator=decorator)
mzi = gf.partial(gf.components.mzi, splitter=mmi1x2, bend=bend_euler, straight=straight)
mzi10 = mzi(delta_length=10)
mzi10.plot()
[14]:
FabB#
FabB has photonic waveguides that require many cladding layers to avoid dopants
Lets say that the waveguides are defined in layer (2, 0) and are 0.3um wide
[15]:
import gdsfactory as gf
from gdsfactory.cross_section import strip
from gdsfactory.difftest import difftest
WIDTH = 0.3
LAYER = (2, 0)
LAYERS_CLADDING = ((71, 0), (68, 0))
fab_b_metal = gf.partial(
strip,
width=WIDTH,
layer=LAYER,
layers_cladding=LAYERS_CLADDING,
)
fab_b_metal.__name__ = "fab_b_metal"
straight = gf.partial(gf.components.straight, cross_section=fab_b_metal)
bend_euler = gf.partial(gf.components.bend_euler, cross_section=fab_b_metal)
mmi1x2 = gf.partial(
gf.components.mmi1x2,
cross_section=fab_b_metal,
width=WIDTH,
width_taper=WIDTH,
width_mmi=4 * WIDTH,
)
mzi = gf.partial(gf.components.mzi, cross_section=fab_b_metal, splitter=mmi1x2)
gc = gf.partial(
gf.components.grating_coupler_elliptical_te, layer=LAYER, wg_width=WIDTH
)
def test_waveguide():
c = gf.components.straight(cross_section=fab_b_metal)
difftest(c)
c = mzi()
wg_gc = gf.routing.add_fiber_array(
component=c, grating_coupler=gc, cross_section=fab_b_metal
)
wg_gc.plot()
[15]:
FabC#
Lets assume that fab C has both Silicon and Silicon Nitride components, and you need different waveguide widths for C and O band.
Lets asume that O band nitride waveguide width is 0.9 and Cband Nitride waveguide width is 1um, and for 0.4um for Silicon O band and 0.5um for silicon Cband.
Lets also that this foundry has an LVS flow where all components have optical pins defined in layer (100, 0)
[16]:
from typing import Callable, Dict, Optional, Tuple
import pydantic.dataclasses as dataclasses
import gdsfactory as gf
from gdsfactory.add_pins import add_pin_square_inside
from gdsfactory.component import Component, ComponentReference
from gdsfactory.cross_section import strip
from gdsfactory.tech import LayerLevel, LayerStack, Tech
from gdsfactory.types import Layer
@dataclasses.dataclass(frozen=True)
class LayerMap:
WG: Layer = (10, 1)
WG_CLAD: Layer = (10, 2)
WGN: Layer = (34, 0)
WGN_CLAD: Layer = (36, 0)
PIN: Layer = (100, 0)
LAYER = LayerMap()
WIDTH_NITRIDE_OBAND = 0.9
WIDTH_NITRIDE_CBAND = 1.0
PORT_TYPE_TO_LAYER = dict(optical=(100, 0))
def get_layer_stack_fab_c(thickness: float = 350.0) -> LayerStack:
"""Returns generic LayerStack"""
return LayerStack(
core=LayerLevel(
layer=(34, 0),
thickness=350.0,
zmin=220.0 + 100.0,
),
clad=LayerLevel(layer=(36, 0)),
)
def add_pins(
component: Component,
function: Callable = add_pin_square_inside,
pin_length: float = 0.5,
port_layer: Layer = LAYER.PIN,
**kwargs,
) -> None:
"""Add Pin port markers.
Args:
component: to add ports
function:
pin_length:
port_layer:
function: kwargs
"""
for p in component.ports.values():
function(
component=component,
port=p,
layer=port_layer,
layer_label=port_layer,
pin_length=pin_length,
**kwargs,
)
# cross_sections
fabc_nitride_cband = gf.partial(
strip, width=WIDTH_NITRIDE_CBAND, layer=LAYER.WGN, layers_cladding=(LAYER.WGN_CLAD,)
)
fabc_nitride_oband = gf.partial(
strip, width=WIDTH_NITRIDE_OBAND, layer=LAYER.WGN, layers_cladding=(LAYER.WGN_CLAD,)
)
# LEAF COMPONENTS have pins
mmi1x2_nitride_c = gf.partial(
gf.components.mmi1x2,
width=WIDTH_NITRIDE_CBAND,
cross_section=fabc_nitride_cband,
decorator=add_pins,
)
mmi1x2_nitride_o = gf.partial(
gf.components.mmi1x2,
width=WIDTH_NITRIDE_OBAND,
cross_section=fabc_nitride_oband,
decorator=add_pins,
)
bend_euler_c = gf.partial(
gf.components.bend_euler, cross_section=fabc_nitride_cband, decorator=add_pins
)
straight_c = gf.partial(
gf.components.straight, cross_section=fabc_nitride_cband, decorator=add_pins
)
bend_euler_o = gf.partial(
gf.components.bend_euler, cross_section=fabc_nitride_oband, decorator=add_pins
)
straight_o = gf.partial(
gf.components.straight, cross_section=fabc_nitride_oband, decorator=add_pins
)
gc_nitride_c = gf.partial(
gf.components.grating_coupler_elliptical_te,
grating_line_width=0.6,
wg_width=WIDTH_NITRIDE_CBAND,
layer=LAYER.WGN,
decorator=add_pins,
)
# HIERARCHICAL COMPONENTS made of leaf components
mzi_nitride_c = gf.partial(
gf.components.mzi,
cross_section=fabc_nitride_cband,
splitter=mmi1x2_nitride_c,
decorator=add_pins,
straight=straight_c,
bend=bend_euler_c,
)
mzi_nitride_o = gf.partial(
gf.components.mzi,
cross_section=fabc_nitride_oband,
splitter=mmi1x2_nitride_c,
decorator=add_pins,
straight=straight_o,
bend=bend_euler_o,
)
[17]:
mzi = mzi_nitride_c()
mzi_gc = gf.routing.add_fiber_single(
component=mzi,
grating_coupler=gc_nitride_c,
cross_section=fabc_nitride_cband,
optical_routing_type=1,
straight=straight_c,
bend=bend_euler_c,
)
mzi_gc.plot()
[17]:
[18]:
ls = get_layer_stack_fab_c()
[19]:
ls.to_dict()
[19]:
{'core': {'layer': (34, 0),
'thickness': 350.0,
'zmin': 320.0,
'material': None,
'sidewall_angle': 0},
'clad': {'layer': (36, 0),
'thickness': None,
'zmin': None,
'material': None,
'sidewall_angle': 0}}
3D rendering#
You can also render components in 3D.
You will need to define two things:
LayerStack: for each layer contains thickness of each material and z position
LayerSet: for each layer contains colors (this file is the same that Klayout uses). You can load it with
gf.layers.load_lyp()
[20]:
heater = gf.components.straight_heater_metal(length=50)
heater.plot()
[20]:
[21]:
scene = gf.export.to_3d(component=heater, layer_set=gf.layers.LAYER_SET)
scene.show()
/usr/share/miniconda/envs/anaconda-client-env/lib/python3.9/site-packages/IPython/core/display.py:419: UserWarning: Consider using IPython.display.IFrame instead
warnings.warn("Consider using IPython.display.IFrame instead")
[21]:
[ ]: